/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.dtgenerator.core.rtgen.entitygen.method;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.dtgenerator.core.rtgen.entitygen.entityimpl.BefEntityImplGenCtx;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.dynamicProp.DynamicPropSetImpl;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.overall.Utils;
import com.inspur.edp.cef.spi.common.UdtManagerUtil;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import java.math.BigDecimal;
import java.util.ArrayList;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

public class CreateEntityDataGenerator extends ClassMethodGenerator {

  private MediateType interfaceType;
  private BefEntityImplGenCtx entityImplGenCtx;
  private String objectCode;
  private IGspCommonObject commonObject;

  public CreateEntityDataGenerator(MediateType entityType, BefEntityImplGenCtx entityImplGenCtx,
      String objectCode, IGspCommonObject commonObject) {
    this.interfaceType = entityType;
    this.entityImplGenCtx = entityImplGenCtx;
    this.objectCode = objectCode;
    this.commonObject = commonObject;
  }

  private static final String paramid = "id";

  @Override
  protected ArrayList<ModifierKeyword> getAccessModifiers() {
    return new ArrayList<Modifier.ModifierKeyword>(java.util.Arrays
        .asList(new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PUBLIC_KEYWORD}));
  }

  @Override
  protected boolean getIsOverride() {
    return true;
  }

  @Override
  protected String getMethodName() {
    return "innerCreateEntityData";
  }

  @Override
  protected ArrayList<ParameterInfo> getParameterCollection() {
    ArrayList<ParameterInfo> list = new ArrayList<>();
    ParameterInfo parameterInfo = new ParameterInfo();
    parameterInfo.setParamType(new TypeInfo(String.class));
    parameterInfo.setParamName(paramid);
    list.add(parameterInfo);

    return list;
  }

  @Override
  protected TypeInfo getReturnType() {
    return new TypeInfo(IEntityData.class);
  }

  @Override
  protected Block buildMethodBody() {
    if (interfaceType == null) {
      interfaceType = entityImplGenCtx.getParentContext().getChildInterfaceTypes().get(objectCode);
    }

    Block block = ast.newBlock();
    addNewDataStatement(block);
    //addSetIdPropertyStatement(block);

    addSetNestedPropertiesStatements(block);

    addReturnDataStatement(block);
    return block;
  }

  private void addNewDataStatement(Block block) {
    VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
    variableDeclarationFragment.setName(ast.newSimpleName("data"));

    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.setType(ast.newSimpleType(ast.newName(
        entityImplGenCtx.getParentContext().getProjContext().getEntityDataMediateTypes()
            .get(objectCode).getFullName())));
    variableDeclarationFragment.setInitializer(classInstanceCreation);

    VariableDeclarationStatement variableDeclarationStatement = ast
        .newVariableDeclarationStatement(variableDeclarationFragment);
    variableDeclarationStatement.setType(ast.newSimpleType(ast.newName(
        entityImplGenCtx.getParentContext().getProjContext().getEntityDataMediateTypes()
            .get(objectCode).getFullName())));
    block.statements().add(variableDeclarationStatement);
  }
//
//  private void addSetIdPropertyStatement(Block block) {
//    MethodInvocation methodInvocation = ast.newMethodInvocation();
//    methodInvocation.setName(ast.newSimpleName("setID"));
//    methodInvocation.setExpression(ast.newSimpleName("data"));
//    methodInvocation.arguments().add(ast.newSimpleName(paramid));
//    block.statements().add(ast.newExpressionStatement(methodInvocation));
//  }

  private void addSetNestedPropertiesStatements(Block block) {
    String objectCode = this.objectCode;

    MediateType interfaceType = null;
    if (((GspBizEntityObject)commonObject).getIsRootNode()) {
      interfaceType = entityImplGenCtx.getEntityGenCtx().getProjContext().getCefGenContext()
          .getEntityGenContext().getRootNodeType();
    } else {
      interfaceType = entityImplGenCtx.getEntityGenCtx().getProjContext().getCefGenContext()
          .getEntityGenContext().getNodeTypes().get(objectCode);
    }
    for (IGspCommonField element : commonObject.getContainElements()) {
      if (element.getIsUdt()) {
        addSetNestedPropertyStatement(objectCode, block, element);
      } else if (element.getObjectType() == GspElementObjectType.DynamicProp) {
        addSetDynamicPropSetStatement(ast, block, element, entityImplGenCtx);
      }
    }
  }

  private void addSetNestedPropertyStatement(String objectCode, Block block,
      IGspCommonField element) {
    MethodInvocation methodInvocation = ast.newMethodInvocation();
    methodInvocation.setName(ast.newSimpleName(Utils.getSetterMethodName(element.getLabelID())));
    methodInvocation.setExpression(ast.newSimpleName("data"));

    if (!containRefElementNotFromAssoUdt(element)) {
      addCreateDataTypeStatement(methodInvocation, objectCode, element);
    } else {
      addCreateEnrichedAssoUdtStatement(methodInvocation, objectCode, element);
    }
    block.statements().add(ast.newExpressionStatement(methodInvocation));
  }

  private boolean containRefElementNotFromAssoUdt(IGspCommonField element) {
    UnifiedDataTypeDef udtDef = entityImplGenCtx.getParentContext().getProjContext()
        .getCefGenContext().getMetadataContent(element.getUdtPkgName(), element.getUdtID());
    SimpleDataTypeDef sdtDef = (SimpleDataTypeDef) ((udtDef instanceof SimpleDataTypeDef) ? udtDef
        : null);
    return sdtDef != null && sdtDef.getObjectType() == GspElementObjectType.Association && element
        .containRefElementNotFromAssoUdt();
  }

  private void addCreateEnrichedAssoUdtStatement(MethodInvocation methodInvocation,
      String objectCode, IGspCommonField element) {
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.setType(ast.newSimpleType(ast.newName(
        entityImplGenCtx.getParentContext().getEnrichedUdtAssoTypes().get(objectCode)
            .get(element.getLabelID()).getFullName())));
    classInstanceCreation.arguments().add(ast.newNullLiteral());
    methodInvocation.arguments().add(classInstanceCreation);
  }

  private void addCreateDataTypeStatement(MethodInvocation methodInvocation, String objectCode,
      IGspCommonField element) {
    MethodInvocation createDataInvocation = ast.newMethodInvocation();
    createDataInvocation.setName(ast.newSimpleName("createData"));
    createDataInvocation.setExpression(ast.newName(UdtManagerUtil.class.getTypeName()));
    UnifiedDataTypeDef udt = entityImplGenCtx.getEntityGenCtx().getProjContext().getCefGenContext()
        .getMetadataContent(element.getUdtPkgName(), element.getUdtID());
    createDataInvocation.arguments().add(ExpressionUtils.getStringLiteral(ast, udt.getUdtType()));

    if (udt instanceof SimpleDataTypeDef && !((GspBizEntityElement) element).getIsDefaultNull()) {
      Expression defaultValueExpr = getSimpleUdtDefaultValue(ast, (SimpleDataTypeDef) udt);
      if (defaultValueExpr != null) {
        createDataInvocation.arguments().add(defaultValueExpr);
      }
    }
    else if(udt instanceof SimpleDataTypeDef && element.getObjectType() == GspElementObjectType.Enum && ((GspBizEntityElement) element).getIsDefaultNull()){
      createDataInvocation.arguments().add(ast.newNullLiteral());
    }

    methodInvocation.arguments().add(createDataInvocation);
  }

  static Expression getSimpleUdtDefaultValue(AST ast, SimpleDataTypeDef udt) {
    if (udt.getObjectType() == GspElementObjectType.None) {
      if (udt.getMDataType() == GspElementDataType.String) {
        StringLiteral stringLiteral = ast.newStringLiteral();
        stringLiteral.setLiteralValue("");
        return stringLiteral;
      } else if (udt.getMDataType() == GspElementDataType.Boolean) {
        return ast.newBooleanLiteral(false);
      } else if (udt.getMDataType() == GspElementDataType.Integer) {
        return ast.newNumberLiteral("0");
      } else if (udt.getMDataType() == GspElementDataType.Decimal) {
        ClassInstanceCreation cls = ast.newClassInstanceCreation();
        cls.setType(new TypeInfo(BigDecimal.class) {{
          setUseFullName(true);
        }}.getType(ast));
        StringLiteral stringLiteral1 = ast.newStringLiteral();
        stringLiteral1.setLiteralValue("0.0");
        cls.arguments().add(stringLiteral1);
        return cls;
      }
    }
    return null;
  }

  private void addReturnDataStatement(Block block) {
    ReturnStatement returnStatement = ast.newReturnStatement();
    returnStatement.setExpression(ast.newSimpleName("data"));
    block.statements().add(returnStatement);
  }

  public static void addSetDynamicPropSetStatement(AST ast, Block block, IGspCommonField field,
      BefEntityImplGenCtx entityImplGenCtx) {
    MethodInvocation methodInvocation = ast.newMethodInvocation();
    methodInvocation.setName(ast.newSimpleName(Utils.getSetterMethodName(field.getLabelID())));
    methodInvocation.setExpression(ast.newSimpleName("data"));
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.setType(new TypeInfo(DynamicPropSetImpl.class)
        .getType(ast, entityImplGenCtx.getParentContext().getProjContext()));
    methodInvocation.arguments().add(classInstanceCreation);
    block.statements().add(ast.newExpressionStatement(methodInvocation));
  }
}
